Skip to content

Give more types to CachedOperation to avoid use of any#3342

Merged
robertbrignull merged 3 commits intomainfrom
robertbrignull/cached_operation
Feb 13, 2024
Merged

Give more types to CachedOperation to avoid use of any#3342
robertbrignull merged 3 commits intomainfrom
robertbrignull/cached_operation

Conversation

@robertbrignull
Copy link
Copy Markdown
Contributor

This PR removes the uses of any in CachedOperation. Unfortunately just replacing the use of any with unknown is not enough, but we can make it work if we add a type parameter for the function arguments.

I've made it work by having two type arguments. One for the arguments and another for the return type. Hopefully this isn't too cumbersome. I did manage to remove one set of explicit type args from template-provider.ts so now we only specify them once when declaring the variable and have them inferred when actually constructing the CachedOperation object.

I wanted to do it with one argument so you could do CachedOperation<typeof this.getDefinitions>, but sadly I couldn't get this to work. The issue I hit was that the async function didn't like a return type of ReturnType<T> and claimed it wasn't a Promise. It's possible we could get this to work with one type arg, but the effort didn't seem worth it to me.

Checklist

  • CHANGELOG.md has been updated to incorporate all user visible changes made by this pull request.
  • Issues have been created for any UI or other user-facing changes made by this pull request.
  • [Maintainers only] If this pull request makes user-facing changes that require documentation changes, open a corresponding docs pull request in the github/codeql repo and add the ready-for-doc-review label there.

@robertbrignull robertbrignull requested a review from a team February 12, 2024 15:30
@robertbrignull robertbrignull requested a review from a team as a code owner February 12, 2024 15:30
Copy link
Copy Markdown
Member

@koesie10 koesie10 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

I wanted to do it with one argument so you could do CachedOperation<typeof this.getDefinitions>, but sadly I couldn't get this to work. The issue I hit was that the async function didn't like a return type of ReturnType<T> and claimed it wasn't a Promise. It's possible we could get this to work with one type arg, but the effort didn't seem worth it to me.

I was able to get this working using Awaited<ReturnType<T>>, but ran into some issues when using F extends (t: string, ...args: unknown[]) => Promise<unknown> as the type argument since it seems like the args: unknown[] doesn't work with typeof this.getDefinitions. I was able to work around this by using two type arguments, one of which is fully optional and just an implementation detail, but it makes it a little bit harder to understand intuitively. I've got this implementation in this branch. Please let me know if I should create a PR for this, I'm not entirely sure if this makes it clearer (although it does make the CachedOperation easier to use).


/**
* A cached mapping from strings to value of type U.
* A cached mapping from args of type [string, S] to a value of type Promise<U>.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the original comment here was correct: only the string is used as the cache key, and any other arguments are not taken into account for the cache itself, only for calling the operation.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, thanks for pointing that out. I'll adjust the comment to be more accurate.

@robertbrignull
Copy link
Copy Markdown
Contributor Author

I was able to work around this by using two type arguments, one of which is fully optional and just an implementation detail, but it makes it a little bit harder to understand intuitively. I've got this implementation in this branch. Please let me know if I should create a PR for this, I'm not entirely sure if this makes it clearer (although it does make the CachedOperation easier to use).

Thanks for having a go at this and working it out. I also don't know if this makes it clearer or not. I'm also not sure if my current PR makes things better. As your comments in that branch make clear, we're going to a lot of effort to remove the use of any and it makes me wonder if it's worth it.

I'm also considering whether it's better to rewrite CachedOperations types to something like

CachedOperation<T extends (t: string, ...args: any[]) => Promise<any>>

and then tell eslint to ignore this one. It works but it still leads to doing Promise<Awaited<ReturnType<T>>> which is quite a mouthful. This could be alleviated by introducing a second type parameter like U = Awaited<ReturnType<T>> just to cut down on repetition of that sequence, but then we've got an extra arg.

Interestingly in terms of total lines of code (which obviously is not a perfect metric), this PR is the smallest.

What I'm going to do right now is merge this PR because it fixes the problem at hand (including the error type) without creating too much code churn. This area is very easy to change so we could definitely do another PR to change things again. I'm not opposed to another PR, but I am wondering if it's best to just move on as it'll have little benefit 🤷🏼

@robertbrignull robertbrignull merged commit be61664 into main Feb 13, 2024
@robertbrignull robertbrignull deleted the robertbrignull/cached_operation branch February 13, 2024 10:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants